package com.selen.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.selen.dto.*;
import com.selen.entities.*;
import com.selen.exception.ServiceException;
import com.selen.mapper.EmpMapper;
import com.selen.mapper.PermissionMapper;
import com.selen.mapper.RoleMapper;
import com.selen.service.EmpService;
import com.selen.utils.JWTUtil;
import com.selen.utils.MinioUtil;
import com.selen.vo.LoginVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * @author Selen
 */
@Service
@CacheConfig(cacheNames = "emp")
public class EmpServiceImpl implements EmpService {

    @Autowired
    private EmpMapper empMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(allEntries = true)
    @Override
    public int insert(EmpInsertDTO dto) {
        Emp emp = BeanUtil.copyProperties(dto, Emp.class);
        // 密码加密
        emp.setPassword(SecureUtil.md5(emp.getPassword()));
        // 设置默认值
        String idcard = emp.getIdcard();
        emp.setAvatar("default-avatar.png");
        emp.setGender(IdcardUtil.getGenderByIdCard(idcard));
        emp.setAge(IdcardUtil.getAgeByIdCard(idcard));
        emp.setProvince(IdcardUtil.getProvinceByIdCard(idcard));
        emp.setAddress("暂未添加详细住址");
        emp.setInfo("暂未添加详细介绍");
        emp.setDeleted(0);
        emp.setCreated(LocalDateTime.now());
        emp.setUpdated(LocalDateTime.now());
        int result = empMapper.insert(emp);
        if (result <= 0) {
            throw new ServiceException("DB添加失败");
        }
        return result;
    }

    @Cacheable(key = "#id", condition = "#id != null ", unless = "#result == null ")
    @Override
    public Emp selectById(Long id) {
        Emp result = empMapper.selectById(id);
        if (ObjectUtil.isNull(result)){
            throw new ServiceException("ID不存在");
        }
        return result;
    }

    @CacheEvict(allEntries = true)
    @Override
    public int updateById(EmpUpdateDTO dto) {
        Emp emp = BeanUtil.copyProperties(dto, Emp.class);
        emp.setCreated(null);
        emp.setUpdated(LocalDateTime.now());
        int result = empMapper.updateById(emp);
        if (result <= 0){
            throw new ServiceException("DB 修改失败");
        }
        return result;
    }

    @CacheEvict(allEntries = true)
    @Override
    public int logicDeleteById(Long id) {
        // todo
        int result = empMapper.logicDeleteById(id);
        if (result <= 0){
            throw new ServiceException("DB 逻辑删除失败");
        }
        return result;
    }

    @CacheEvict(allEntries = true)
    @Override
    public int logicDeleteByIds(List<Long> ids) {
        // todo
        int result = empMapper.logicDeleteByIds(ids);
        if (result <= 0){
            throw new ServiceException("DB 批量逻辑删除失败");
        }
        return result;
    }

    @Cacheable(key = "#root.methodName", unless = "#result == null ")
    @Override
    public List<Emp> selectAll() {
        return empMapper.selectAll();
    }

    @Cacheable(key = "#root.methodName + ':' + #dto.pageNum + ':' + #dto.pageSize ",
            unless = "#result == null ")
    @Override
    public PageInfo<Emp> page(PageDTO dto) {
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        return new PageInfo<>(empMapper.selectAll());
    }

    @Override
    public LoginVO login(LoginDTO dto) {
        Emp emp = BeanUtil.copyProperties(dto, Emp.class);
        String password = emp.getPassword();
        emp.setPassword(SecureUtil.md5(password));
        Emp empRes = empMapper.selectByAccount(emp);
        if (ObjectUtil.isNull(empRes)){
            throw new ServiceException("账户或密码错误");
        }
        String token = JWTUtil.build(empRes.getId(), empRes.getUsername(), empRes.getAvatar());
        List<Permission> permissions = permissionMapper.selectByEmpId(empRes.getId());
        return new LoginVO(token, permissions, empRes);
    }

    @Cacheable(key = "#root.methodName + ':' + " +
            "#dto.getKeyword() + ':' + " +
            "#dto.getPageNum() + ':' + " +
            "#dto.getPageSize()", unless = "#result == null ")
    @Override
    public PageInfo<Emp> dimSelect(EmpPageDTO dto) {
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        return new PageInfo<>(empMapper.selectByDimName(dto.getKeyword()));
    }

    @CacheEvict(allEntries = true)
    @Override
    public String uploadAvatar(EmpUploadDTO dto) {
        final String DIR = "avatar";
        final String BUCKET_NAME = "myclub";
        MultipartFile avatarFile = dto.getAvatarFile();
        Long id = dto.getId();

        // DB 查询
        Emp emp = empMapper.selectById(id);
        if (ObjectUtil.isNull(emp)){
            throw new ServiceException("员工不存在");
        }

        // 从 MINIO 中删除旧文件，默认头像不删
        String oldAvatar = emp.getAvatar();
        if (!("default-avatar.png".equals(oldAvatar))){
            MinioUtil.delete(oldAvatar, DIR, BUCKET_NAME);
        }

        // 将文件上传到MINIO中
        String newAvatar = MinioUtil.randomFilename(avatarFile);
        MinioUtil.upload(avatarFile, newAvatar, DIR, BUCKET_NAME);

        // DB修改
        emp.setAvatar(newAvatar);
        if (empMapper.updateById(emp) <= 0) {
            throw new ServiceException("DB修改失败");
        }
        return newAvatar;
    }

    @CacheEvict(allEntries = true)
    @Override
    public String resetPassword(Long id) {
        String newPassword = "123456789";
        Emp emp = new Emp();
        emp.setId(id);
        emp.setPassword(SecureUtil.md5(newPassword));
        emp.setUpdated(LocalDateTime.now());
        if (empMapper.updateById(emp) <= 0){
            throw new ServiceException("DB 密码重置失败");
        }
        return newPassword;
    }

    @Cacheable(key = "#root.methodName + ':' + #id", unless = "#result == null ")
    @Override
    public List<Role> getRoles(Long id) {
        return roleMapper.selectByEmpId(id);
    }

    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(allEntries = true)
    @Override
    public int updateRoles(EmpRoleDTO dto) {
        Long id = dto.getId();
        List<Long> ids = dto.getRoleIds();
        // 删除员工的中间表记录
        /*
        if (roleMapper.logicDeleteByEmpId(id) <= 0){
            throw new ServiceException("删除角色失败");
        }
        */
        roleMapper.logicDeleteByEmpId(id);
        // 循环添加角色列表
        for (Long roleId : ids){
            EmpRole empRole = new EmpRole();
            empRole.setFkEmpId(id);
            empRole.setFkRoleId(roleId);
            empRole.setDeleted(0);
            empRole.setCreated(LocalDateTime.now());
            empRole.setUpdated(LocalDateTime.now());
            if (roleMapper.insertEmpRole(empRole) <= 0){
                throw new ServiceException("添加角色失败");
            }
        }
        return 1;
    }

    @CacheEvict(allEntries = true)
    @Override
    public int updatePassword(EmpUpdatePasswordDTO dto) {
        Long id = dto.getId();
        String oldPassword = SecureUtil.md5(dto.getOldPassword());
        String newPassword = SecureUtil.md5(dto.getNewPassword());
        // DB 修改
        int result = empMapper.UpdatePassword(newPassword, oldPassword, id);
        if (result <= 0){
            throw new ServiceException("修改密码失败");
        }
        return result;
    }
}
